﻿//#define NGUI

#if UNITY_EDITOR
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.Assertions;
using UnityEngine.UI;
using Object = UnityEngine.Object;

namespace UFrame.AutoQuote
{
    /// <summary>
    /// 支持List 格式:正确格式#变量名_类名#List
    /// </summary>
    [CustomEditor(typeof(MonoAutoQuote), true)]
    public class AutoQuoteEditor : Editor
    {
        public override void OnInspectorGUI()
        {
            base.OnInspectorGUI();

            if (GUILayout.Button("关联子节点引用"))
            {
                Component c = target as Component;
                if (c != null)
                    AutoQuoteMenu.AutoSetRef(c, c.gameObject);
            }
        }
    }


    public static class AutoGenerateSet
    {
        public static bool IsNGUI = false;
    }

    public partial class AutoQuoteMenu
    {
        private static string result = "";


        public static List<Type> generateScript = new List<Type>();

        public static void AddAutoRef(Type type)
        {
            generateScript.Add(type);
        }

        public static string GetFieldValue(string field)
        {
            //string deng = "=";

            if (field == "string")
            {
                return "= string.Empty";
            }
            else if (field == "String")
            {
                return "= string.Empty";
            }
            else if (field == "Int32" || field == "Int16" || field == "Int64")
            {
                return "= 0";
            }
            else if (field == "Float")
            {
                return "= 0f";
            }
            else if (field == "Double")
            {
                return "= 0.0";
            }
            else
            {
                return "= null";
            }
        }

        public static void AutoSetRef(Component c, GameObject o, bool clear = false)
        {
            Type t = c.GetType();
            var infoList = GetFieldInfoAry<MonoBehaviour>(t);
            string name = string.Empty;
            StringBuilder errorText = new StringBuilder();

            List<PropertyModification> pList = new List<PropertyModification>();
            SerializedObject s = new SerializedObject(c);
            //PrefabUtility.RecordPrefabInstancePropertyModifications(o);
            string oPath = AssetDatabase.GetAssetPath(o);
            foreach (var item in infoList)
            {
                Type fieldType = item.FieldType;
                string fieldName = item.Name;
                Type aryType = null;

                if (AutoQuoteTool.IsContainAttribute<SerializeField>(item) == false && item.IsPublic == false)
                    continue;

                if (AutoQuoteTool.IsSubClass<MonoBehaviour>(fieldType)
                    || AutoQuoteTool.IsSubClass<GameObject>(fieldType)
                    || AutoQuoteTool.IsSubClass<IEnumerable>(fieldType))
                {
                    if (item.Name.StartsWith(AutoQuoteAsset.I.mFieldStart))
                        name = AutoQuoteAsset.I.mFrontPrefix + item.Name.Substring(1);
                    else
                        name = AutoQuoteAsset.I.mFrontPrefix + item.Name;

                    if (fieldType.IsArray)
                    {
                        name = name + "_" + fieldType.Name.Replace("[]", "") + "#" + "Ary";
                        aryType = fieldType.GetElementType();
                    }
                    else if (AutoQuoteTool.IsSubClass<IEnumerable>(fieldType))
                    {
                        if (fieldType.GetGenericArguments().Length > 0)
                        {
                            aryType = fieldType.GetGenericArguments()[0];
                            name = name + "_" + aryType.Name + "#List";
                        }
                    }

                    Transform tr = FindEnd(o.transform, name);
                    if (tr == null)
                    {
                        errorText.AppendLine(c.name + "中" + name + "引用没找到引用");
                        continue;
                    }

                    object val = null;
                    if (clear == false)
                    {
                        IEnumerable list = null;
                        if (fieldType.IsArray || AutoQuoteTool.IsSubClass<IEnumerable>(fieldType))
                        {
                            Object[] ary = null;
                            int count = 0;
                            if (AutoQuoteTool.IsSubClass<IEnumerable>(fieldType))
                            {
                                if (AutoQuoteTool.IsSubClass<GameObject>(aryType))
                                {
                                    ary = new Object[tr.transform.childCount];
                                    for (int i = 0; i < tr.transform.childCount; i++)
                                    {
                                        ary[i] = tr.transform.GetChild(i).gameObject;
                                    }
                                }
                                else if (AutoQuoteTool.IsSubClass<Transform>(aryType))
                                {
                                    ary = new Object[tr.transform.childCount];
                                    for (int i = 0; i < tr.transform.childCount; i++)
                                    {
                                        ary[i] = tr.transform.GetChild(i);
                                    }
                                }
                                else
                                    ary = tr.GetComponentsInChildren(aryType);
                            }
                            else
                            {
                                ary = tr.GetComponentsInChildren(fieldType);
                            }

                            if (ary != null)
                                count = ary.Length;

                            list = GetIE(fieldType, ary, count);
                            val = list;
                        }
                        else if (AutoQuoteTool.IsSubClass<GameObject>(fieldType))
                            val = tr.gameObject;
                        else if (AutoQuoteTool.IsSubClass<MonoBehaviour>(fieldType))
                            val = tr.GetComponent(fieldType);
                    }

                    try
                    {
                     

                      
                        SerializedProperty sp = s.FindProperty(item.Name);
                        if(sp.propertyType == SerializedPropertyType.ObjectReference){
                            sp.objectReferenceValue = val as UnityEngine.Object;
                            Debug.Log("设置:object " + val);
                            
                            PrefabUtility.ApplyPropertyOverride(sp,oPath,InteractionMode.AutomatedAction);
                        }
                        
                       
                        
                        //SerializedPropertyTool.SetValue(sp, item, c, val);
                        item.SetValue(c, val);
                    }
                    catch (Exception ex)
                    {
                        Debug.Log(ex.Message);
                    }
                }
            }

            var p = c.transform.localPosition;
            p.x += 0.000000001f;
            c.transform.localPosition = p;
            SerializedObject s2 = new SerializedObject(c.transform);
            s2.FindProperty("m_LocalPosition").vector3Value = p;
            s2.ApplyModifiedProperties();
                
            s.ApplyModifiedProperties();
            //s.ApplyModifiedProperties();
            //PropertyModification pm = new PropertyModification();
            //pList.Add(pm);
            //PrefabUtility.SetPropertyModifications(o, pList.ToArray());
            
            if (errorText.Length > 0)
                Debug.LogError(errorText);
        }

        public static void AutoCreateCode(Component o)
        {
            string goName = o.name;
            if (goName.StartsWith("#"))
                goName = goName.TrimStart('#');

            List<Transform> infoList = new List<Transform>();
            GetTransform(o.transform, infoList);

            StringBuilder fieldText = new StringBuilder();
            StringBuilder eventText = new StringBuilder();
            StringBuilder funcText = new StringBuilder();
            StringBuilder partialFuncText = new StringBuilder();
            StringBuilder registerText = new StringBuilder();

            string nodeName = null;
            string field = null;
            int twoWayGenerateIndex = 0;
            foreach (var item in infoList)
            {
                if (item.name == o.name)
                    continue;

                if (item.gameObject.name.StartsWith("#"))
                {
                    nodeName = item.gameObject.name.TrimStart('#');
                    if (nodeName.Contains("_"))
                    {
                        nodeName = nodeName.Substring(0, nodeName.IndexOf("_"));
                    }

                    string startChar = string.Empty;
                    if (AutoQuoteAsset.I.mFieldStart == "m")
                        startChar = nodeName.Substring(0, 1).ToUpper();
                    else
                        startChar = nodeName.Substring(0, 1).ToLower();

                    string end = string.Empty;
                    if (nodeName.Length > 1)
                        end = nodeName.Substring(1);

                    var g = GetGenerateInfo(item);
                    field = g.Field;
                    string fieldName = AutoQuoteAsset.I.mFieldStart + startChar + end;

                    if (string.IsNullOrEmpty(field))
                        continue;

                    string value = GetFieldValue(field);

                    fieldText.AppendLine(string.Format(g.FieldText, field, fieldName, value));

                    string registerStr = string.Empty;

                    if (g.IsGenerateEvent)
                    {
                        //另外一种方式
                        //eventText.AppendLine(string.Format("public event System.Action<{0}> On{1};", field, nodeName + "Event"));
                        //funcText.AppendLine(string.Format("private void " + mEventPrefix + "{0}({1} go)", nodeName, field) +
                        //    @"{
                        //    if(null != On" + nodeName + @"Event)
                        //    {
                        //        On" + nodeName + @"Event(go);
                        //    }
                        //}
                        //");
                        string upperStr = nodeName.Substring(0, 1).ToUpper() + nodeName.Substring(1);
                        string methodName = AutoQuoteAsset.I.mEventPrefix + upperStr;

                        //分布方法
                        partialFuncText.AppendLine(string.Format(
                            "partial void " + AutoQuoteAsset.I.mEventPrefix + "{0}({1} go);", upperStr,
                            field));


                        if (g.IsArry || g.IsList)
                        {
                            if (AutoGenerateSet.IsNGUI)
                                registerStr = string.Format(AutoQuoteAsset.I.mGTwoAryNGUIEvent,
                                    fieldName, g.LenStr, methodName, fieldName,
                                    field, fieldName, g.EventStr);
                            else
                                registerStr = string.Format(AutoQuoteAsset.I.mUGUIAryEvent,
                                    fieldName, g.LenStr, fieldName, g.EventStr,
                                    methodName, fieldName);
                        }
                        else
                        {
                            switch (field)
                            {
                                case "UIInput":
                                case "UIButton":
                                case "UIToggle":
                                    registerStr = string.Format(AutoQuoteAsset.I.mGTwoButtonEvent, methodName,
                                        fieldName, field, fieldName,
                                        g.EventStr);
                                    break;

                                case "Button":
                                case "Toggle":
                                case "Dropdown":
                                case "Slider":
                                case "Scrollbar":
                                    registerStr = string.Format(AutoQuoteAsset.I.mUGUIButtonEvent, fieldName,
                                        g.EventStr, methodName,
                                        fieldName);
                                    break;
                            }
                        }
                    }


                    if (twoWayGenerateIndex == 0 && AutoGenerateSet.IsNGUI)
                        registerText.AppendLine("EventDelegate delRegist = null;");

                    twoWayGenerateIndex++;

                    if (string.IsNullOrEmpty(registerStr) == false)
                    {
                        registerText.AppendLine(registerStr);
                    }
                }
            }

            string className = o.GetType().Name;
            string baseClassName = o.GetType().BaseType.Name;
            string fileName = className + "A.cs";
            string filePath = Application.dataPath + AutoQuoteAsset.I.mSavePath + "/" + fileName;
            string temp = AutoQuoteAsset.I.classTemp;


            temp = temp.Replace("//@Field", fieldText.ToString() + "\n" + eventText.ToString());
            temp = temp.Replace("//@Init", registerText.ToString());
            temp = temp.Replace("//@Func", funcText.ToString());
            temp = temp.Replace("//@PartialMethod", partialFuncText.ToString());
            temp = temp.Replace("//@ClassName", className + ":" + baseClassName);


            File.WriteAllText(filePath, temp, Encoding.UTF8);
            Debug.Log("输出成功" + filePath);
            AssetDatabase.Refresh();
        }

        public void GenerateEvent()
        {
            // if (g.IsGenerateEvent)
            // {
            //     //另外一种方式
            //     //eventText.AppendLine(string.Format("public event System.Action<{0}> On{1};", field, nodeName + "Event"));
            //     //funcText.AppendLine(string.Format("private void " + mEventPrefix + "{0}({1} go)", nodeName, field) +
            //     //    @"{
            //     //    if(null != On" + nodeName + @"Event)
            //     //    {
            //     //        On" + nodeName + @"Event(go);
            //     //    }
            //     //}
            //     //");
            //     string upperStr = nodeName.Substring(0, 1).ToUpper() + nodeName.Substring(1);
            //     string methodName = AutoQuoteAsset.I.mEventPrefix + upperStr;
            //
            //     //分布方法
            //     partialFuncText.AppendLine(string.Format(
            //         "partial void " + AutoQuoteAsset.I.mEventPrefix + "{0}({1} go);", upperStr,
            //         field));
            //
            //
            //     if (g.IsArry || g.IsList)
            //     {
            //         if (AutoGenerateSet.IsNGUI)
            //             registerStr = string.Format(AutoQuoteAsset.I.mGTwoAryNGUIEvent,
            //                 fieldName, g.LenStr, methodName, fieldName,
            //                 field, fieldName, g.EventStr);
            //         else
            //             registerStr = string.Format(AutoQuoteAsset.I.mUGUIAryEvent,
            //                 fieldName, g.LenStr, fieldName, g.EventStr,
            //                 methodName, fieldName);
            //     }
            //     else
            //     {
            //         switch (field)
            //         {
            //             case "UIInput":
            //             case "UIButton":
            //             case "UIToggle":
            //                 registerStr = string.Format(AutoQuoteAsset.I.mGTwoButtonEvent, methodName,
            //                     fieldName, field, fieldName,
            //                     g.EventStr);
            //                 break;
            //
            //             case "Button":
            //             case "Toggle":
            //             case "Dropdown":
            //                 registerStr = string.Format(AutoQuoteAsset.I.mUGUIButtonEvent, fieldName,
            //                     g.EventStr, methodName,
            //                     fieldName);
            //                 break;
            //         }
            //     }
            // }
        }

        public static GenerateInfo GetGenerateInfo(Transform item)
        {
            GenerateInfo g = new GenerateInfo();

            //数据和list情况
            string aryType = null;
            if (item.name.Contains("_"))
            {
                try
                {
                    aryType = item.name.Substring(item.name.LastIndexOf("_") + 1);
                    string[] ary = aryType.Split('#');
                    g.Field = ary[0];
                    if (ary[1] == "List")
                    {
                        g.IsList = true;
                        g.LenStr = "Count";
                        g.FieldText = "[SerializeField] private List<{0}> {1} {2};";
                    }
                    else if (ary[1] == "Ary")
                    {
                        g.IsArry = true;
                        g.LenStr = "Length";
                        g.FieldText = "[SerializeField] private {0} [] {1} {2};";
                    }
                    else
                    {
                        Debug.LogError("格式错误: " + item.name + " 正确格式#变量名_类名#List");
                        Assert.IsTrue(false);
                    }

                    IsGenerateEvent(g);
                    return g;
                }
                catch
                {
                    //Debug.LogError("格式错误: " + item.name + " 正确格式#变量名_类名#List");
                    //UnityEngine.Assertions.Assert.IsTrue(false);

                    Debug.LogError("不要使用下划线命名,会导致错误生成");
                    Assert.IsTrue(false);
                }
            }

            var cAry = item.GetComponents<Component>();
            foreach (var com in cAry)
            {
                bool isGenerate = IsAutoQuote(com.GetType());

                if (isGenerate)
                {
                    g.Field = com.GetType().Name;
                    g.IsCustomStruct = true;
                    break;
                }
            }

            if (g.Field == null)
            {
                Component monoB = null;
                List<string> lList = new List<string>()
                {
                    "UIInput",
                    "UIButton",
                    "UIProgressBar",
                    "UIToggle",
                    "UISprite",
                    "UILabel",
                    //这里添加对类型的支持
                    "InputField",
                    "Dropdown",
                    "Button",
                    "Toggle",
                    "Text",
                    "RawImage",
                    "Scrollbar",
                    "Slider",
                    "Image",
                };

                for (int i = 0; i < lList.Count; i++)
                {
                    monoB = item.GetComponent(lList[i]);
                    if (monoB != null)
                    {
                        g.Field = monoB.GetType().Name;
                        break;
                    }
                }
            }

            if (g.Field == null)
                g.Field = "GameObject";

            g.FieldText = AutoQuoteAsset.I.fieldTemp;
            IsGenerateEvent(g);
            return g;
        }

        private static IEnumerable GetIE(Type fieldType, Object[] objList, int count)
        {
            Type aryType = null;
            if (fieldType.IsGenericType)
                aryType = fieldType.GetGenericArguments()[0];
            if (fieldType.IsArray)
                aryType = fieldType.GetElementType();


            List<object> listObject = new List<object>();
            IEnumerable list = null;


            //System.Reflection.Assembly assembly = new System.Reflection.Assembly("");
            if (aryType != null && fieldType.IsArray)
            {
                var test = Array.CreateInstance(aryType, count);
                for (int i = 0; i < test.Length; i++)
                    test.SetValue(objList[i], i);
                return test;
            }
            else
            {
                Type type = typeof(List<>).MakeGenericType(aryType);
                object dd = Activator.CreateInstance(type);
                IList ilist = dd as IList;
                for (int i = 0; i < objList.Length; i++)
                    ilist.Add(objList[i]);
                return ilist;
            }
        }

        private static void IsGenerateEvent(GenerateInfo g)
        {
            switch (g.Field)
            {
                case "Button":
                    g.EventStr = "onClick";
                    g.IsGenerateEvent = true;
                    break;
                case "Toggle":
                    g.EventStr = "onValueChanged";
                    g.IsGenerateEvent = true;
                    break;
                case "Text":
                    //g.EventStr = "onValueChanged";
                    g.IsGenerateEvent = false;
                    break;
                case "Dropdown":
                    g.EventStr = "onValueChanged";
                    g.IsGenerateEvent = true;
                    break;
                case "Slider":
                    g.EventStr = "onValueChanged";
                    g.IsGenerateEvent = true;
                    break;
                case "Scrollbar":
                    g.EventStr = "onValueChanged";
                    g.IsGenerateEvent = true;
                    break;
                case "UIButton":
                    g.EventStr = "onClick";
                    g.IsGenerateEvent = true;
                    break;
                case "UIInput":
                    g.EventStr = "onChange";
                    g.IsGenerateEvent = true;
                    break;
                case "UIToggle":
                    g.EventStr = "onChange";
                    g.IsGenerateEvent = true;
                    break;
                default:
                    g.IsGenerateEvent = false;
                    break;
            }
        }


        public static List<FieldInfo> GetFieldInfoAry<T>(Type t) where T : class
        {
            List<FieldInfo> fieldInfoList = new List<FieldInfo>();
            var i = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
            FieldInfo[] infoAry = t.GetFields(i);
            fieldInfoList.AddRange(infoAry);
            Type baseType = t.BaseType;
            while (baseType != null)
            {
                infoAry = baseType.GetFields(i);
                fieldInfoList.AddRange(infoAry);
                baseType = baseType.BaseType;
                if (baseType == null)
                    break;
                if (baseType.FullName == typeof(T).FullName)
                    break;
            }

            return fieldInfoList;
        }

        private static void GetNodePath(Transform trans, ref string path)
        {
            if (path == "")
            {
                path = trans.name;
            }
            else
            {
                path = trans.name + "/" + path;
            }

            if (trans.parent != null)
            {
                GetNodePath(trans.parent, ref path);
            }
        }

        public static string GetFiles(DirectoryInfo directory, string fileName, string pattern)
        {
            if (directory.Exists || pattern.Trim() != string.Empty)
            {
                foreach (FileInfo info in directory.GetFiles(pattern))
                {
                    if (info.Name == fileName)
                    {
                        result = info.FullName;
                        break;
                    }
                }

                foreach (DirectoryInfo info in directory.GetDirectories())
                {
                    GetFiles(info, fileName, pattern);
                }
            }

            string returnString = result;
            return returnString;
        }

        private static Transform FindEnd(Transform t, string name)
        {
            Transform tr = t.Find(name);
            if (tr == null)
            {
                //var oList = t.GetComponentsInChildren(typeof(Transform), true);

                List<Transform> oList = new List<Transform>();
                GetTransform(t, oList);

                foreach (var node in oList)
                {
                    if (node.name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
                    {
                        tr = node.transform;
                        break;
                    }
                }
            }

            return tr;
        }

        private static void GetTransform(Transform o, List<Transform> l)
        {
            Transform temp = null;
            Component[] cList = null;
            bool isFindChild = true;
            for (int i = 0; i < o.childCount; i++)
            {
                temp = o.GetChild(i);
                l.Add(temp);
                isFindChild = true;
                cList = temp.GetComponents<Component>();
                foreach (var item in cList)
                {
                    bool isGenerate = IsAutoQuote(item.GetType());

                    if (isGenerate)
                    {
                        isFindChild = false;
                        break;
                    }
                }

                if (isFindChild)
                    GetTransform(temp, l);
            }
        }
    }


    public class GenerateInfo
    {
        public bool IsGenerateEvent { get; set; }
        public string Field { get; set; }
        public bool IsCustomStruct { get; set; }
        public bool IsArry { get; set; }
        public bool IsList { get; set; }

        public string LenStr { get; set; }
        public string EventStr { get; set; }

        public string FieldText { get; set; }
    }
}
#endif